﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace 请求响应加密.Tools
{
    public class TimeWheelScheduler : IDisposable
    {
        private readonly int _tickIntervalMs; // 每个槽的时间间隔(毫秒)
        private readonly int _slotCount;     // 时间轮槽数量
        private readonly List<ScheduledTask>[] _wheel; // 时间轮槽数组
        private int _currentSlot;            // 当前指针指向的槽
        private readonly Timer _timer;       // 驱动时间轮的计时器
        private readonly object _lock = new object();
        private bool _isRunning;

        public TimeWheelScheduler(int tickIntervalMs = 1000, int slotCount = 60)
        {
            if (tickIntervalMs <= 0)
                throw new ArgumentOutOfRangeException(nameof(tickIntervalMs), "触发间隔必须大于0");
            if (slotCount <= 0)
                throw new ArgumentOutOfRangeException(nameof(slotCount), "插槽数量必须大于0");

            _tickIntervalMs = tickIntervalMs;
            _slotCount = slotCount;
            _wheel = new List<ScheduledTask>[slotCount];
            for (int i = 0; i < slotCount; i++)
            {
                _wheel[i] = new List<ScheduledTask>();
            }

            _timer = new Timer(Tick, null, Timeout.Infinite, Timeout.Infinite);
            _isRunning = false;
        }

        public void Start()
        {
            lock (_lock)
            {
                if (_isRunning) return;

                _isRunning = true;
                _timer.Change(0, _tickIntervalMs);
            }
        }

        public void Stop()
        {
            lock (_lock)
            {
                if (!_isRunning) return;

                _isRunning = false;
                _timer.Change(Timeout.Infinite, Timeout.Infinite);
            }
        }

        /// <summary>
        /// 调度一个一次性任务
        /// </summary>
        public void ScheduleOnce(Action task, TimeSpan delay)
        {
            ScheduleInternal(task, delay, null);
        }

        /// <summary>
        /// 调度一个周期性任务
        /// </summary>
        public void ScheduleRecurring(Action task, TimeSpan delay, TimeSpan interval)
        {
            ScheduleInternal(task, delay, interval);
        }

        private void ScheduleInternal(Action task, TimeSpan initialDelay, TimeSpan? recurringInterval)
        {
            if (task == null)
                throw new ArgumentNullException(nameof(task));
            if (initialDelay.TotalMilliseconds <= 0)
                throw new ArgumentOutOfRangeException(nameof(initialDelay), "延迟时间必须大于0");

            lock (_lock)
            {
                var totalDelayMs = (int)initialDelay.TotalMilliseconds;
                var ticks = totalDelayMs / _tickIntervalMs;

                var targetSlot = (_currentSlot + ticks) % _slotCount;

                var scheduledTask = new ScheduledTask
                {
                    Task = task,
                    RemainingRounds = ticks / _slotCount,
                    RecurringInterval = recurringInterval,
                    OriginalIntervalTicks = recurringInterval.HasValue ? (int)recurringInterval.Value.TotalMilliseconds / _tickIntervalMs : 0
                };

                _wheel[targetSlot].Add(scheduledTask);
            }
        }

        /// <summary>
        /// 在指定时间点执行一次性任务
        /// </summary>
        public void ScheduleAt(Action task, DateTime executeAt)
        {
            var delay = executeAt - DateTime.Now;
            if (delay.TotalMilliseconds <= 0)
            {
                // 如果指定时间已过，立即执行
                Task.Run(task);
                return;
            }
            ScheduleInternal(task, delay, null);
        }

        /// <summary>
        /// 每天固定时间执行任务  
        ///     var dailyTime = new TimeSpan(16, 30, 0); // 下午4:30
        /// </summary>
        public void ScheduleDaily(Action task, TimeSpan timeOfDay)
        {
            var now = DateTime.Now;
            var todayExecuteAt = now.Date.Add(timeOfDay);
            var executeAt = now > todayExecuteAt ? todayExecuteAt.AddDays(1) : todayExecuteAt;

            // 使用递归方式实现每天执行
            Action wrapper = () =>
            {
                try
                {
                    task.Invoke();
                }
                finally
                {
                    ScheduleDaily(task, timeOfDay); // 重新调度下一天
                }
            };

            ScheduleAt(wrapper, executeAt);
        }

        /// <summary>
        /// 使用Cron表达式调度周期性任务
        /// </summary>
        public void ScheduleCron(Action task, string cronExpression)
        {
            if (task == null)
                throw new ArgumentNullException(nameof(task));

            var parser = new CronExpressionParser(cronExpression);
            ScheduleCronInternal(task, parser);
        }

        private void ScheduleCronInternal(Action task, CronExpressionParser parser)
        {
            var now = DateTime.Now;
            var nextRunTime = parser.GetNextOccurrence(now);
            var delay = nextRunTime - now;

            // 使用递归实现周期性调度
            Action wrapper = null;
            wrapper = () =>
            {
                try
                {
                    task.Invoke();
                }
                finally
                {
                    // 计算下一次执行时间并重新调度
                    var newNextRunTime = parser.GetNextOccurrence(DateTime.Now);
                    var newDelay = newNextRunTime - DateTime.Now;
                    ScheduleInternal(wrapper, newDelay, null);
                }
            };

            ScheduleInternal(wrapper, delay, null);
        }

        private void Tick(object state)
        {
            lock (_lock)
            {
                if (!_isRunning) return;

                var currentSlotTasks = _wheel[_currentSlot];

                // 处理当前槽的所有任务
                for (int i = currentSlotTasks.Count - 1; i >= 0; i--)
                {
                    var task = currentSlotTasks[i];

                    if (task.RemainingRounds == 0)
                    {
                        // 执行任务
                        Task.Run(() =>
                        {
                            try
                            {
                                task.Task.Invoke();
                            }
                            catch (Exception ex)
                            {
                                // 处理任务执行异常
                                Console.WriteLine($"任务执行失败: {ex}");
                            }
                        });

                        // 如果是周期性任务，重新调度
                        if (task.RecurringInterval.HasValue)
                        {
                            var newTask = new ScheduledTask
                            {
                                Task = task.Task,
                                RemainingRounds = task.OriginalIntervalTicks / _slotCount,
                                RecurringInterval = task.RecurringInterval,
                                OriginalIntervalTicks = task.OriginalIntervalTicks
                            };

                            var targetSlot = (_currentSlot + task.OriginalIntervalTicks) % _slotCount;
                            _wheel[targetSlot].Add(newTask);
                        }

                        currentSlotTasks.RemoveAt(i);
                    }
                    else
                    {
                        task.RemainingRounds--;
                    }
                }

                // 移动指针到下一个槽
                _currentSlot = (_currentSlot + 1) % _slotCount;
            }
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }

        private class ScheduledTask
        {
            public Action Task { get; set; }
            public int RemainingRounds { get; set; }
            public TimeSpan? RecurringInterval { get; set; }
            public int OriginalIntervalTicks { get; set; }
            public bool IsAbsoluteTime { get; set; }
        }
    }
}
